/**
 * 
 */
package com.myschool.server.web.controller;

import java.io.IOException;
import java.io.PrintWriter;
import java.text.ParseException;
import java.text.SimpleDateFormat;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.expressme.openid.Association;
import org.expressme.openid.Authentication;
import org.expressme.openid.Endpoint;
import org.expressme.openid.OpenIdException;
import org.expressme.openid.OpenIdManager;
import org.myschool.common.exception.ApplicationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.myschool.server.manager.UserManager;
import com.myschool.server.model.Contacts;
import com.myschool.server.model.RegistrationStatus;
import com.myschool.server.model.User;
import com.myschool.server.web.command.UserSession;

/**
 * @author Hegdes
 * 
 */
@Controller
public class LoginController {
	static final long ONE_HOUR = 3600000L;
	static final long TWO_HOUR = ONE_HOUR * 2L;
	static final String ATTR_MAC = "openid_mac";
	static final String ATTR_ALIAS = "openid_alias";

	@Autowired
	UserManager userManager;

	OpenIdManager manager;

	public void init() {

		manager = new OpenIdManager();
		manager.setRealm("http://localhost:8080/myschool/"); // change to your
																// domain
		manager.setReturnTo("http://localhost:8080/myschool/login.htm"); // change
																			// to
																			// your
																			// servlet
																			// url
	}

	@RequestMapping("login/forgotPassword.htm")
	public @ResponseBody
	ModelMap forgotPassword(Contacts contact) {

		ModelMap map = new ModelMap();

		String message = "Email sent to your email address.Please follow the instructions.";
		try {
			this.userManager.forgotPassword(contact.getEmail());

		} catch (ApplicationException exp) {
			message = "Email doesn't exist,Please provide the right email address.";

		}
		map.addAttribute("message", message);
		return map;
	}

	@RequestMapping("login/processLogin.htm")
	public @ResponseBody
	ModelMap processLogin(User user, HttpServletRequest request,
			HttpServletResponse response) {

		User persistantUser = this.userManager.retrieveUser(user);

		ModelMap map = new ModelMap();
		if (persistantUser != null) {
			map.addAttribute("message", "Login successfull");
			map.addAttribute("registrationStatus", persistantUser
					.getRegistrationStatus().name());

			addUserToSession(request, persistantUser);

		} else {
			map.addAttribute("message",
					"Could not login to myschool.Please check your credentials.");
		}

		return map;

	}

	@RequestMapping("login/createAccount.htm")
	public @ResponseBody
	ModelMap createAccount(User user, HttpServletRequest request) {

		ModelMap map = new ModelMap();
		try {
			// new account,so not registered.
			user.setRegistrationStatus(RegistrationStatus.NR);
			this.userManager.create(user);

			addUserToSession(request, user);

			map.addAttribute("result", "success");
			map.addAttribute("message", "User created successfully");

		} catch (ApplicationException e) {
			e.printStackTrace();
			map.addAttribute("result", "failure");
			map.addAttribute("message", "Error occured :" + e.getMessage());
		} catch (RuntimeException e) {
			e.printStackTrace();
			map.addAttribute("result", "failure");
			map.addAttribute("message", "Error occured :" + e.getMessage());
		} finally {

			user = null;
		}

		return map;

	}

	private void addUserToSession(HttpServletRequest request, User user) {
		UserSession userSession = new UserSession(user.getEntityId(),
				user.getUsername(), user.getType(),
				user.getSchool() != null ? user.getSchool().getEntityId()
						: null);
		request.getSession(false).setAttribute(UserSession.CURRENT_USER,
				userSession);

	}

	@RequestMapping("openidLogin.htm")
	public String openIdLogin(HttpServletRequest request,
			HttpServletResponse response, User user) throws IOException,
			ServletException {

		System.out.println("path:" + request.getContextPath());

		System.out.println("User name=" + user.getUsername());

		System.out.println("OP =" + user.getOpenIdProvider());

		System.out.println("Oppeennnnnnnnnnnnn id:");
		init();
		String op = user.getOpenIdProvider();
		if (op == null) {
			// check nonce:
			checkNonce(request.getParameter("openid.response_nonce"));
			// get authentication:
			byte[] mac_key = (byte[]) request.getSession().getAttribute(
					ATTR_MAC);
			String alias = (String) request.getSession().getAttribute(
					ATTR_ALIAS);
			Authentication authentication = manager.getAuthentication(request,
					mac_key, alias);
			String identity = authentication.getIdentity();
			String email = authentication.getEmail();
			// TODO: create user if not exist in database:
			showAuthentication(response.getWriter(), identity, email);
		} else if ("Google".equals(op)) {
			// redirect to Google sign on page:
			Endpoint endpoint = manager.lookupEndpoint("Google");
			Association association = manager.lookupAssociation(endpoint);
			request.getSession().setAttribute(ATTR_MAC,
					association.getRawMacKey());
			request.getSession().setAttribute(ATTR_ALIAS, endpoint.getAlias());
			String url = manager.getAuthenticationUrl(endpoint, association);
			return "redirect:" + url;
			// response.sendRedirect(url);
		} else if ("Yahoo".equals(op)) {
			// redirect to Google sign on page:
			Endpoint endpoint = manager.lookupEndpoint("Yahoo");
			Association association = manager.lookupAssociation(endpoint);
			request.getSession().setAttribute(ATTR_MAC,
					association.getRawMacKey());
			request.getSession().setAttribute(ATTR_ALIAS, endpoint.getAlias());
			String url = manager.getAuthenticationUrl(endpoint, association);
			// response.sendRedirect(url);
			return "redirect:" + url;

		} else {
			throw new ServletException("Bad parameter op=" + op);
		}

		return "A new user with id " + user.getUsername()
				+ " has been created successfully";

	}

	void showAuthentication(PrintWriter pw, String identity, String email) {
		pw.print("<html><body><h1>Identity</h1><p>");
		pw.print(identity);
		pw.print("</p><h1>Email</h1><p>");
		pw.print(email == null ? "(null)" : email);
		pw.print("</p></body></html>");
		pw.flush();
	}

	void checkNonce(String nonce) {
		// check response_nonce to prevent replay-attack:
		if (nonce == null || nonce.length() < 20)
			throw new OpenIdException("Verify failed.");
		long nonceTime = getNonceTime(nonce);
		long diff = System.currentTimeMillis() - nonceTime;
		if (diff < 0)
			diff = (-diff);
		if (diff > ONE_HOUR)
			throw new OpenIdException("Bad nonce time.");
		if (isNonceExist(nonce))
			throw new OpenIdException("Verify nonce failed.");
		storeNonce(nonce, nonceTime + TWO_HOUR);
	}

	boolean isNonceExist(String nonce) {
		// TODO: check if nonce is exist in database:
		return false;
	}

	void storeNonce(String nonce, long expires) {
		// TODO: store nonce in database:
	}

	long getNonceTime(String nonce) {
		try {
			return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(
					nonce.substring(0, 19) + "+0000").getTime();
		} catch (ParseException e) {
			throw new OpenIdException("Bad nonce time.");
		}
	}

	/**
	 * @return the userManager
	 */

	public UserManager getUserManager() {
		return userManager;
	}

	/**
	 * @param userManager
	 *            the userManager to set
	 */

	public void setUserManager(UserManager userManager) {
		this.userManager = userManager;
	}
}
